Namespace Component

    ''' <summary>
    ''' Color selector in ComboBox style
    ''' </summary>
    ''' <remarks>Simulate VStudio color selector in option dialog.</remarks>
    Public Class ColorPicker
        Inherits Windows.Forms.ComboBox

        Public Event SelectedColorChanged(ByVal sender As Object, ByVal e As SelectedColorChangedEventArgs)

        Private Const CST_COLORRECT_MARGIN_LEFT As Integer = 4 'Margin of the left bound.
        Private Const CST_COLORRECT_MARGIN As Integer = 2 'Margin of right, top and bottom bound.
        Private Const CST_COLORSTRING_PADDING As Integer = 2 'Padiing with color square and color string.

        ''' <summary>
        ''' Kind of collection in combo list.
        ''' </summary>
        ''' <remarks></remarks>
        Public Enum ColorCollectionKind
            ''' <summary>
            ''' All color defined in System.Drawing.
            ''' </summary>
            ''' <remarks></remarks>
            All = 1
            ''' <summary>
            ''' The same items in VStudio option.
            ''' </summary>
            ''' <remarks></remarks>
            VStudio = 2
            ''' <summary>
            ''' Add color item manually.
            ''' </summary>
            ''' <remarks></remarks>
            Manual = 3
        End Enum

        Private m_pKind As ColorCollectionKind 'Picker kind of color items.
        Private m_enabledDefault As Boolean 'Indicate enable default item or not.
        Private m_defaultColor As Drawing.Color 'Indicate the default color.

#Region " Public Properties "

        ''' <summary>
        ''' Constructor.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
            'Execute base class instructor.
            MyBase.New()
            'Disabled auto draw mode.
            Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
            Me.DropDownStyle = Windows.Forms.ComboBoxStyle.DropDown
            'Set defualt value.
            Me.PickerKind = ColorCollectionKind.VStudio
            Me.m_enabledDefault = False
            Me.m_defaultColor = Drawing.Color.Empty
        End Sub

        ''' <summary>
        ''' Picker kind of color items.
        ''' </summary>
        ''' <value>Set the picker kind of color items.</value>
        ''' <returns>Get the picker kind of color items.</returns>
        ''' <remarks></remarks>
        <ComponentModel.Category("iSoft"), _
         ComponentModel.DefaultValue(ColorCollectionKind.VStudio)> _
        Public Property PickerKind() As ColorCollectionKind
            Get
                Return Me.m_pKind
            End Get
            Set(ByVal value As ColorCollectionKind)
                Me.SetPickerKind(value)
            End Set
        End Property

        ''' <summary>
        ''' Indicate enable default item or not.
        ''' </summary>
        ''' <value>Set the indicate enable default item or not.</value>
        ''' <returns>Get the indicate enable default item or not.</returns>
        ''' <remarks></remarks>
        <ComponentModel.Category("iSoft"), _
         ComponentModel.DefaultValue(False)> _
        Public Property EnbaledDefault() As Boolean
            Get
                Return Me.m_enabledDefault
            End Get
            Set(ByVal value As Boolean)
                Me.m_enabledDefault = value
            End Set
        End Property

        ''' <summary>
        ''' Indicate the default color.
        ''' </summary>
        ''' <value>Set the default color.</value>
        ''' <returns>Get the default color.</returns>
        ''' <remarks></remarks>
        <ComponentModel.Category("iSoft")> _
        Public Property DefaultColor() As Drawing.Color
            Get
                Return Me.m_defaultColor
            End Get
            Set(ByVal value As Drawing.Color)
                Me.m_defaultColor = value
            End Set
        End Property

        ''' <summary>
        ''' Selected Color.
        ''' </summary>
        ''' <value>Set the selected Color.</value>
        ''' <returns>Get the selected Color.</returns>
        ''' <remarks></remarks>
        <ComponentModel.Category("iSoft"), _
         ComponentModel.Browsable(False)> _
        Public Property SelectedColor() As Drawing.Color
            Get
                Return Me.GetSelectedColor()
            End Get
            Set(ByVal value As Drawing.Color)
                Me.SetSelectedColor(value)
            End Set
        End Property

#End Region

        ''' <summary>
        ''' Get the selected color.
        ''' </summary>
        ''' <returns>Selected color.</returns>
        ''' <remarks></remarks>
        Private Function GetSelectedColor() As Drawing.Color
            Dim c As Drawing.Color = Drawing.Color.Empty
            If Me.SelectedItem IsNot Nothing AndAlso TypeOf Me.SelectedItem Is ColorItem Then
                c = CType(Me.SelectedItem, ColorItem).Color
            End If
            Return c
        End Function

        ''' <summary>
        ''' Set the selected color.
        ''' </summary>
        ''' <param name="c">Color want to select.</param>
        ''' <remarks>If the color is not in list, it will be set into 'Custom' item.</remarks>
        Private Sub SetSelectedColor(ByVal c As Drawing.Color)
            Dim pos As Integer
            'Selected color can not be Empty.
            If c = Drawing.Color.Empty Then
            Else
                pos = Me.IndexOf(c)
                If pos >= 0 Then
                    'Set the selected index of found in list.
                    If Me.SelectedIndex = pos Then
                    Else
                        Me.SelectedIndex = pos
                    End If
                Else
                    'Append or modyfy Custom item and then select it.
                    Me.UpdateCustomItem(c)
                End If
                'Raise SelectedColorChanged event
                RaiseEvent SelectedColorChanged(Me, New SelectedColorChangedEventArgs(c))
            End If
        End Sub

        ''' <summary>
        ''' Get the index of the color in the list.
        ''' </summary>
        ''' <param name="c">Color to find.</param>
        ''' <returns>The position of the color. Not existed will return -1.</returns>
        ''' <remarks></remarks>
        Private Function IndexOf(ByVal c As Drawing.Color) As Integer
            Dim ret As Integer = -1
            Dim i As Integer
            Dim ci As ColorItem
            For i = 0 To Me.Items.Count - 1
                ci = Me.Items(i)
                If ci.Color.ToArgb = c.ToArgb Then
                    ret = i
                    Exit For
                End If
            Next
            Return ret
        End Function

        ''' <summary>
        ''' Upadte the 'Custom' item value.
        ''' </summary>
        ''' <param name="c">Color object.</param>
        ''' <remarks>
        ''' If 'Custom' item is existed, it will be modified with c;
        ''' And it will be added when 'Custom' is not existed.</remarks>
        Private Sub UpdateCustomItem(ByVal c As Drawing.Color)
            Dim ci As ColorItem = Nothing
            'Find the custom item.
            For Each _ci As ColorItem In Me.Items
                If _ci.Kind = ColorItem.ColorItemKind.Custom Then
                    ci = _ci
                    Exit For
                End If
            Next
            'Create the custom item if not found.
            If ci Is Nothing Then
                ci = New ColorItem(c, ColorItem.ColorItemKind.Custom)
                Me.Items.Add(ci)
            Else
                ci.Color = c
            End If
            'Select the cutom item.
            Me.SelectedItem = ci
            Me.Refresh()
        End Sub

        ''' <summary>
        ''' Get the 'Custom' item position index of the list.
        ''' </summary>
        ''' <returns>The 'Custom' item position index of the list. -1 if not found.</returns>
        ''' <remarks></remarks>
        Private Function GetCustomItemIndex() As Integer
            Dim ret As Integer = -1
            Dim i As Integer
            Dim ci As ColorItem
            For i = 0 To Me.Items.Count - 1
                ci = Me.Items(i)
                If ci.Kind = ColorItem.ColorItemKind.Custom Then
                    ret = i
                    Exit For
                End If
            Next
            Return ret
        End Function

        ''' <summary>
        ''' Set the pick kind and fill the color list automaticlly.
        ''' </summary>
        ''' <param name="kind">The pick kind.</param>
        ''' <remarks></remarks>
        Private Sub SetPickerKind(ByVal kind As ColorCollectionKind)
            Select Case kind
                Case ColorCollectionKind.All
                    Me.SetColorItemsAll()
                Case ColorCollectionKind.VStudio
                    Me.SetColorItemsVStudio()
                Case Else
                    Me.Items.Clear()
            End Select
            Me.m_pKind = kind
        End Sub

        ''' <summary>
        ''' Fill the combo items with all colors listed in System.Drawing.Color.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub SetColorItemsAll()
            Dim tColor As Type = GetType(Drawing.Color)
            Dim propInf As Reflection.PropertyInfo
            Dim objPropVal As Object
            'Clear all items in combo.
            Me.Items.Clear()
            'Add default item if indecated.
            Me.AddDefaultColorItem()
            'Reflect the properties in Color type.
            For Each propInf In tColor.GetProperties
                objPropVal = propInf.GetValue(propInf, Nothing)
                If TypeOf objPropVal Is Drawing.Color Then
                    Me.Items.Add(New ColorItem(CType(objPropVal, Drawing.Color), ColorItem.ColorItemKind.Normal))
                End If
            Next
        End Sub

        ''' <summary>
        ''' Add the 'Default' item to the list.
        ''' </summary>
        ''' <remarks>The 'Default' item will be added if EnabledDefault = True.</remarks>
        Private Sub AddDefaultColorItem()
            If Me.m_enabledDefault = True Then
                Me.Items.Add(New ColorItem(Me.m_defaultColor, ColorItem.ColorItemKind.Default))
            End If
        End Sub

        ''' <summary>
        ''' Fill the combo items with the colors listed in VStudio option dialog.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub SetColorItemsVStudio()
            'Clear all items in combo.
            Me.Items.Clear()
            'Add default item if indecated.
            Me.AddDefaultColorItem()
            'Add the color listed in VStudio color list.
            Me.Items.Add(New ColorItem(Drawing.Color.Black, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.White, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Maroon, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Green, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Olive, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Navy, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Purple, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Teal, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Silver, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Gray, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Red, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Lime, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Yellow, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Blue, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Magenta, ColorItem.ColorItemKind.Normal))
            Me.Items.Add(New ColorItem(Drawing.Color.Cyan, ColorItem.ColorItemKind.Normal))
        End Sub

        ''' <summary>
        ''' Raise SelectedColorChanged event.
        ''' </summary>
        ''' <remarks></remarks>
        Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
            MyBase.OnSelectedIndexChanged(e)
            Dim ci As ColorItem = Me.SelectedItem
            If ci IsNot Nothing Then
                RaiseEvent SelectedColorChanged(Me, New SelectedColorChangedEventArgs(ci.Color))
            End If
        End Sub

#Region " Draw Item Functions "

        ''' <summary>
        ''' Set the item height and width.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub ColorPicker_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) Handles Me.MeasureItem
            e.ItemHeight = Me.ItemHeight
            e.ItemWidth = Me.DropDownWidth
        End Sub

        ''' <summary>
        ''' Draw the item layout with color square and color name.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub ColorPicker_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem
            Dim ci As ColorItem
            If e.Index >= 0 AndAlso e.Index <= Me.Items.Count - 1 Then
                ci = Me.Items(e.Index)
                'Draw the background of the item.
                e.DrawBackground()
                'Create a square filled with the animals color.
                Dim rectBorder As Drawing.Rectangle = New Drawing.Rectangle(CST_COLORRECT_MARGIN_LEFT, e.Bounds.Top + CST_COLORRECT_MARGIN, e.Bounds.Height - CST_COLORRECT_MARGIN * 2, e.Bounds.Height - CST_COLORRECT_MARGIN * 2)
                Dim rectFill As Drawing.Rectangle = New Drawing.Rectangle(rectBorder.X + 1, rectBorder.Y + 1, rectBorder.Width - 1, rectBorder.Height - 1)
                e.Graphics.DrawRectangle(Drawing.Pens.Black, rectBorder)
                e.Graphics.FillRectangle(New Drawing.SolidBrush(ci.Color), rectFill)
                'Draw the color string.
                e.Graphics.DrawString(ci.ToString, Me.Font, Drawing.Brushes.Black, e.Bounds.X + rectBorder.Width + CST_COLORRECT_MARGIN_LEFT + CST_COLORSTRING_PADDING, e.Bounds.Y + CST_COLORRECT_MARGIN)
                ' Draw the focus rectangle if the mouse hovers over an item.
                e.DrawFocusRectangle()
            End If
        End Sub

#End Region

#Region " Piblic Mathods "

        Public Sub AddColor(ByVal c As Drawing.Color)
            Me.Items.Add(New ColorItem(c, ColorItem.ColorItemKind.Normal))
        End Sub

#End Region

    End Class

End Namespace
